#include <appkit/async.h>
#include <appkit/console.h>
#include <appkit/datetime.h>
#include <appkit/strutil.h>
#include <appkit/thread.h>
#include <appkit/tracer.h>

using namespace appkit;

struct Foobar {
    using Ptr = std::shared_ptr<Foobar>;
    Foobar(std::string id) : m_id(id) {}
    std::string id() { return m_id; }
    std::string m_id;
};

class TestCallback {
    DECL_CLASSMETA(TestCallback)
public:
    void init() {
        m_callback.addCallback(
            std::bind(&TestCallback::callback01, this, std::placeholders::_1));
        m_callback.addCallback(
            std::bind(&TestCallback::callback02, this, std::placeholders::_1));
    }

    void test() {
        while (1) {
            TRACE_YELLOW("01. call once.");
            TRACE_YELLOW("input q to quit!");
            ConsoleIn ci;
            ci.waitInput();
            if (ci.asString() == "01") {
                auto data = std::make_shared<Foobar>("foobar");
                m_callback.callAvailable(data);
            } else if (ci.asString() == "q" || ci.asString() == "quit") {
                break;
            }
        }
    }

    void callback01(Foobar::Ptr data) {
        TRACE_INFO_CLASS("callback -- Foobar(%s)", data->id().data());
    }
    void callback02(Foobar::Ptr data) {
        TRACE_INFO_CLASS("callback -- Foobar(%s)", data->id().data());
    }

private:
    AsyncCallback<Foobar> m_callback;
};

class TestTaskQueue {
    DECL_CLASSMETA(TestTaskQueue)
public:
    void init() {
        m_taskQueue = std::make_shared<AsyncTaskQueue>();
        m_taskQueue->init(5);
    }

    void test() {
        while (1) {
            TRACE_YELLOW("01. add 1 task.");
            TRACE_YELLOW("02. add 20 tasks.");
            TRACE_YELLOW("input q to quit!");
            ConsoleIn ci;
            ci.waitInput();
            if (ci.asString() == "01") {
                m_taskQueue->putTask([&]() {
                    TRACE_INFO_CLASS("task run in thread: 0x%x",
                                     Thread::threadID());
                    Thread::msleep(2000);
                });
            } else if (ci.asString() == "02") {
                for (auto i = 0; i < 20; i++) {
                    m_taskQueue->putTask([&]() {
                        TRACE_INFO_CLASS("task run in thread: 0x%x",
                                         Thread::threadID());
                        Thread::msleep(2000);
                    });
                }
            } else if (ci.asString() == "q" || ci.asString() == "quit") {
                break;
            }
        }
    }

private:
    std::shared_ptr<AsyncTaskQueue> m_taskQueue{nullptr};
};

void test_async() {
    while (1) {
        TRACE_YELLOW("----------async test--------------");
        TRACE_YELLOW("01. async callback test.");
        TRACE_YELLOW("02. async task queue test.");
        TRACE_YELLOW(" q. quit");
        TRACE_YELLOW("please input selection:");
        ConsoleIn ci;
        ci.waitInput();
        if (ci.asString() == "01") {
            TestCallback test;
            test.init();
            test.test();
        } else if (ci.asString() == "02") {
            TestTaskQueue test;
            test.init();
            test.test();
        } else if (ci.asString() == "q" || ci.asString() == "quit") {
            break;
        }
    }
}